<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS-Interpreter RegExp Demo</title>
  <link href="style.css" rel="stylesheet" type="text/css">
  <script src="../acorn.js"></script>
  <script src="../interpreter.js"></script>
  <script>
    var myInterpreter = [];
    function initAlert(interpreter, globalObject) {
      var wrapper = function alert(text) {
        return window.alert(arguments.length ? text : '');
      };
      interpreter.setProperty(globalObject, 'alert',
          interpreter.createNativeFunction(wrapper));
    }

    function parseButton(n) {
      var code = document.getElementById('code' + n).value;
      myInterpreter[n] = new Interpreter(code, initAlert);
      myInterpreter[n].REGEXP_MODE = n;
      disable(n, '');
    }

    function stepButton(n) {
      var start = 0;
      var end = 0;
      var stack = myInterpreter[n].getStateStack();
      if (stack.length) {
        var node = stack[stack.length - 1].node;
        start = node.start;
        end = node.end;
      }
      createSelection(n, start, end);
      try {
        var ok = myInterpreter[n].step();
      } finally {
        if (!ok) {
          disable(n, 'disabled');
        }
      }
    }

    function runButton(n) {
      disable(n, 'disabled');
      if (myInterpreter[n].run()) {
        // Ran until an async call.  Give this call a chance to run.
        // Then start running again later.
        setTimeout(runButton, 100, n);
      }
    }

    function disable(n, disabled) {
      document.getElementById('stepButton' + n).disabled = disabled;
      document.getElementById('runButton' + n).disabled = disabled;
    }

    function createSelection(n, start, end) {
      var field = document.getElementById('code' + n);
      if (field.createTextRange) {
        var selRange = field.createTextRange();
        selRange.collapse(true);
        selRange.moveStart('character', start);
        selRange.moveEnd('character', end);
        selRange.select();
      } else if (field.setSelectionRange) {
        field.setSelectionRange(start, end);
      } else if (field.selectionStart) {
        field.selectionStart = start;
        field.selectionEnd = end;
      }
      field.focus();
    }
  </script>
</head>
<body>
  <h1>JS-Interpreter RegExp Demo</h1>

  <p>Pathological regular expressions can execute in geometric time.
  For example <code>'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.match(/^(a+)+b/)</code>
  will effectively crash many JavaScript runtimes.  JS-Interpreter instances
  have three modes for handling regular expressions.</p>

  <h2>myInterpreter.REGEXP_MODE = 0</h2>

  <p>In mode 0, all regular expressions are disabled and throw an error.  This
  is safe and works in every environment.</p>

  <p><textarea id="code0" spellcheck="false">
// All regular expressions will fail here.
try {
  alert('hello'.search(/(.)\1+/));
} catch (e) {
  alert(e);
}
</textarea><br>
  <button onclick="parseButton(0)">Parse</button>
  <button onclick="stepButton(0)" id="stepButton0" disabled="disabled">Step</button>
  <button onclick="runButton(0)" id="runButton0" disabled="disabled">Run</button>
  </p>

  <h2>myInterpreter.REGEXP_MODE = 1</h2>

  <p>In mode 1, all regular expressions are executed natively.  This
  is potentially <i>unsafe</i> and works in every environment.  Note that to
  exploit this vulnerability the malicious user generally needs to have access
  to setting both the regular expression and the matching string.  And the worst
  case result is just a crash, not an escape from the sandbox.</p>

  <p><textarea id="code1" spellcheck="false">
// This regular expression is fine and returns 'll'.
alert('hello'.match(/(.)\1+/)[0]);
</textarea><br>
  <button onclick="parseButton(1)">Parse</button>
  <button onclick="stepButton(1)" id="stepButton1" disabled="disabled">Step</button>
  <button onclick="runButton(1)" id="runButton1" disabled="disabled">Run</button>
  </p>

  <h2>myInterpreter.REGEXP_MODE = 2 (default mode)</h2>

  <p>In mode 2, all regular expressions are executed in a separate environment.
  In web browsers this means asynchronously in a Web Worker thread.  In Node.js
  this means synchronously in a VM.  If a regular expression takes more than
  myInterpreter.REGEXP_THREAD_TIMEOUT (default 1000 ms) then it is terminated
  and an error thrown.  This is safe and works in most environments.</p>

  <p>If neither Web Workers nor VMs are supported (such as Internet Explorer 9
  or 10, or the JS-Intepreter interpreting itself, or some special enviornments
  such as browser extensions), then all regular expressions will throw an error
  (same as REGEXP_MODE 0).</p>

  <p><textarea id="code2" spellcheck="false">
// This regular expression is fine and returns 'aaac'.
alert('aaac'.split(/^(a+)+b/));
try {
  // This regular expression is pathological and hits the timeout.
  alert('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.split(/^(a+)+b/));
} catch (e) {
  alert(e);
}
</textarea><br>
  <button onclick="parseButton(2)">Parse</button>
  <button onclick="stepButton(2)" id="stepButton2" disabled="disabled">Step</button>
  <button onclick="runButton(2)" id="runButton2" disabled="disabled">Run</button>
  </p>

  <p>Back to the <a href="../docs.html">JS-Interpreter documentation</a>.</p>

</body>
</html>
